/*
 * user_oled_driver.c
 *
 *  Created on: Apr 24, 2024
 *      Author: Lenovo
 */
#include "user_includes.h"
#include "user_oled_driver.h"
#include "ziku_16x16_3500.h"
#include "user_config.h"

#if USER_OLED_EN
#if 0
/**
 * 数据存储格式：
 * 纵向8点，高位在下，先从左到右，再从上到下
 * 每一个Bit对应一个像素点
 *
 *      B0 B0                  B0 B0
 *      B1 B1                  B1 B1
 *      B2 B2                  B2 B2
 *      B3 B3  ------------->  B3 B3 --
 *      B4 B4                  B4 B4  |
 *      B5 B5                  B5 B5  |
 *      B6 B6                  B6 B6  |
 *      B7 B7                  B7 B7  |
 *                                    |
 *  -----------------------------------
 *  |
 *  |   B0 B0                  B0 B0
 *  |   B1 B1                  B1 B1
 *  |   B2 B2                  B2 B2
 *  --> B3 B3  ------------->  B3 B3
 *      B4 B4                  B4 B4
 *      B5 B5                  B5 B5
 *      B6 B6                  B6 B6
 *      B7 B7                  B7 B7
 *
 * 坐标轴定义：
 * 左上角为(0, 0)点
 * 横向向右为X轴，取值范围：0~127
 * 纵向向下为Y轴，取值范围：0~63
 *
 *       0             X轴           127
 *      .------------------------------->
 *    0 |
 *      |
 *      |
 *      |
 *  Y轴 |
 *      |
 *      |
 *      |
 *   63 |
 *      v
 *
 */

void I2cInit(void)
{
    GPIO_ModeCfg(OLED_BL_EN_PORT, OLED_BL_EN_PIN, GPIO_ModeOut_PP_5mA);

    OLED_BL_EN_L();

    GPIOB_ModeCfg( GPIO_Pin_12|GPIO_Pin_13, GPIO_ModeIN_PU );
    I2C_Init( I2C_Mode_I2C, 400000, I2C_DutyCycle_2, I2C_Ack_Enable, I2C_AckAddr_7bit, 0x35 );

    I2C_Cmd(ENABLE);
}

void OledWriteCmd(UINT8 var)
{
    while( I2C_GetFlagStatus( I2C_FLAG_BUSY ) != RESET );

    I2C_GenerateSTART( ENABLE );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT ) );
    I2C_Send7bitAddress( I2C_SLAVE_ADDRESS7, I2C_Direction_Transmitter );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET );
    I2C_SendData( 0x00 );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET );
    I2C_SendData( var );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
    I2C_GenerateSTOP( ENABLE );
}

void OledWriteData(unsigned char var)
{
    while( I2C_GetFlagStatus( I2C_FLAG_BUSY ) != RESET );

    I2C_GenerateSTART( ENABLE );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT ) );
    I2C_Send7bitAddress( I2C_SLAVE_ADDRESS7, I2C_Direction_Transmitter );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET );
    I2C_SendData( 0x40 );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET );
    I2C_SendData( var );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
    I2C_GenerateSTOP( ENABLE );
}

//坐标设置：也就是在哪里显示
void OledSetPos(uint8_t x, uint8_t y)
{
  //以下3个寄存器只在页寻址的模式下有效
  OledWriteCmd(0xb0+y);             //页地址设置     0xb0~0xb7
  OledWriteCmd(((x&0xf0)>>4)|0x10); //列高位地址设置
  OledWriteCmd((x&0x0f));           //列低位地址设置
}

//开启Oled显示
void OledDisplayOn(void)
{
  OledWriteCmd(0X8D);  //SET DCDC命令
  OledWriteCmd(0X14);  //DCDC ON
  OledWriteCmd(0XAF);  //DISPLAY ON
}

//关闭Oled显示
void OledDisplayOff(void)
{
  OledWriteCmd(0X8D);  //SET DCDC命令
  OledWriteCmd(0X10);  //DCDC OFF
  OledWriteCmd(0XAE);  //DISPLAY OFF
}

//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样
void OledClear(void)
{
  uint8_t i,n;

  for(i=0;i<8;i++)
  {
    OledWriteCmd (0xb0+i);    //设置页地址（0~7）
    OledWriteCmd (0x00);      //设置显示位置—列低地址
    OledWriteCmd (0x10);      //设置显示位置—列高地址
    for(n=0;n<128;n++)
      OledWriteData(0);
  } //更新显示
}

//在指定位置显示一个字符,包括部分字符
//x:0~127，y:0~7
//Char_Size:选择字体 16/12
void OledShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
{
  uint8_t c=0,i=0;

  c=chr-' ';//得到偏移后的值
  if(x>MAX_COLUMN-1)
  {
    x=0;
    y=y+2;
  }
  if(Char_Size ==16)
  {
    OledSetPos(x,y);
    for(i=0;i<8;i++)
    {
      OledWriteData(F8X16[c*16+i]);//先写上半部分
    }

    OledSetPos(x,y+1);
    for(i=0;i<8;i++)
    {
      OledWriteData(F8X16[c*16+i+8]);//后写下半部分
    }
  }
  else
  {
    OledSetPos(x,y);
    for(i=0;i<6;i++)
    {
      OledWriteData(F6x8[c][i]);
    }
  }
}

//显示一个字符串
void OledShowString(uint8_t x,uint8_t y,char *str,uint8_t Char_Size)
{
  unsigned char j=0;

  while (str[j]!='\0')
  {
    OledShowChar(x,y,str[j],Char_Size);
    x+=8;
    if(x>120)
    {
      x=0;
      y+=2;
    }
    j++;//移动一次就是一个page，取值0-7
  }
}

//显示汉字
//由于汉字是16*16大小的，所以最多显示4行汉字
//index:在汉字取模中的索引
void OledShowCN(uint8_t x,uint8_t y,uint8_t index)
{
  uint8_t t;

  OledSetPos(x,y);
  for(t=0;t<16;t++)
  {
    OledWriteData(Hzk[index][t]);
  }

  OledSetPos(x,y+1);
  for(t=0;t<16;t++)
  {
    OledWriteData(Hzk[index][t+16]);
  }
}

//显示汉字
//由于汉字是16*16大小的，所以最多显示4行汉字
//index:在汉字取模中的索引
void OledShowCNStr(uint8_t x,uint8_t y, uint8_t *index_table, uint8_t index_num)
{
    int i = 0;

    for(i = 0; i < index_num; i++){
        if(x+i*HZ_WIDTH >127 || y>63){
            break;
        }
        OledShowCN(x+i*HZ_WIDTH, y, index_table[i]);
    }
}


//SSD1306初始化
void OledInit(void)
{
  DelayMs(300);
  DelayMs(300);

  //SSD1306复位之后，默认的是页寻址方式

  OledWriteCmd(0xAE);//--display off

  OledWriteCmd(0x00);//--set low column address
  OledWriteCmd(0x10);//--set high column address
  OledWriteCmd(0x40);//--set start line address

  OledWriteCmd(0xB0);//--set page address

  OledWriteCmd(0x81);// contract control
  OledWriteCmd(0xFF);//--128
  OledWriteCmd(0xA1);//set segment re-map  0 to 127
  OledWriteCmd(0xA6);//set normal  display
  OledWriteCmd(0xA8);//set multiplex ratio(1 to 64)
  OledWriteCmd(0x3F);//--1/32 duty

  OledWriteCmd(0xC8);//Com scan direction
  OledWriteCmd(0xD3);//set display offset
  OledWriteCmd(0x00);//no offset

  OledWriteCmd(0xD5);//set display clock divide ratio/oscillator frequency
  OledWriteCmd(0x80);//

  OledWriteCmd(0xD8);//set area color mode off
  OledWriteCmd(0x05);//

  OledWriteCmd(0xD9);//Set Pre-Charge Period
  OledWriteCmd(0xF1);//

  OledWriteCmd(0xDA);//set com pin  hardware configuartion
  OledWriteCmd(0x12);//

  OledWriteCmd(0xDB);//set Vcomh
  OledWriteCmd(0x30);//0x20,0.77xVcc

  OledWriteCmd(0x8D);//set charge pump enable
  OledWriteCmd(0x14);//

  OledWriteCmd(0xAF);//--turn on Oled panel
}

void OledDisplayTest(void)
{

    I2cInit();
    OledInit();

    OledClear();

    OledDisplayMain();

//    OledShowString(20,0,(char *)("EEWORLD"),16);
//
//    char test[3] = {0x31, 0x32, 0x33};
//
//  //  while(1){
//        OledShowString(20,2,(char *)test,16);
//    //    test[2]++;
//     //   sys_delay_ms(2000);
//        free_dog();
//  //  }

//    OledShowCN(24,0,0);
//    OledShowCN(40,0,1);
//    OledShowCN(56,0,2);
//    OledShowCN(72,0,3);
   // OledShowCNStr(32, 2, welcome_str, 4);
//    OledShowCN(84,0,0);
//    OledShowCN(100,0,1);
//
//    OledShowString(32,3,(char *)("CH582"),16);
//
////    OledShowCN(80,3,2);
////    OledShowCN(96,3,3);
//
//    OledShowString(40,6,(char *)("2022-03-15"),16);

  //  OledShowCN(96, 3 , 20);
}
#else

//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
#define MAX_WAIT_TIMEOUT_CNT    400
uint8_t OLED_GRAM[128][8];
static volatile bool is_init_flag = 0;
static volatile bool is_oled_normal = true;
static volatile bool oled_power_status = 0;

void I2cInit(void)
{
    if(is_init_flag){
        return;
    }
    GPIO_ModeCfg(OLED_BL_EN_PORT, OLED_BL_EN_PIN, GPIO_ModeOut_PP_5mA);

    OLED_BL_EN_L();

    //必须要加延迟,否则无法显示
    DelayMs(30);



   #if 0
       GPIOB_ModeCfg( GPIO_Pin_12|GPIO_Pin_13, GPIO_ModeIN_PU );
   #else
       GPIOPinRemap(ENABLE, RB_PIN_I2C);
       GPIOB_ModeCfg( GPIO_Pin_21|GPIO_Pin_20, GPIO_ModeIN_PU );
   #endif

   // GPIOB_ModeCfg( GPIO_Pin_12|GPIO_Pin_13, GPIO_ModeIN_PU );
    I2C_Init( I2C_Mode_I2C, 800000, I2C_DutyCycle_2, I2C_Ack_Enable, I2C_AckAddr_7bit, 0x35 );

    I2C_Cmd(ENABLE);



    is_init_flag = 1;
}

void I2cUninit(void)
{
    if(!is_init_flag){
        return;
    }
    OLED_BL_EN_H();
    is_init_flag = 0;
    is_oled_normal = true;
    GPIOB_ModeCfg( GPIO_Pin_21|GPIO_Pin_20, GPIO_ModeIN_PD );

    DEBUG_Printf("I2cUninit\r\n");
}
__attribute__((section(".highcode")))
void OLED_Write_Cmd(UINT8 var)
{
    uint16_t cnt = 0;

    if(!is_oled_normal){
        DEBUG_Printf("oled unnormal\r\n");
        return;
    }

    cnt = 0;
    while( I2C_GetFlagStatus( I2C_FLAG_BUSY ) != RESET && (cnt++<=MAX_WAIT_TIMEOUT_CNT));

    if(cnt >= MAX_WAIT_TIMEOUT_CNT){
        is_oled_normal = false;
        DEBUG_Printf("1\r\n");
        while(1);
        return ;
    }

    I2C_GenerateSTART( ENABLE );

    cnt = 0;
    while( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT ) && (cnt++<MAX_WAIT_TIMEOUT_CNT));

    if(cnt >= MAX_WAIT_TIMEOUT_CNT){
        is_oled_normal = false;
        return ;
    }

    I2C_Send7bitAddress( I2C_SLAVE_ADDRESS7, I2C_Direction_Transmitter );

    cnt = 0;
    while( !I2C_CheckEvent( I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) && (cnt++<MAX_WAIT_TIMEOUT_CNT));
    if(cnt >= MAX_WAIT_TIMEOUT_CNT){
        is_oled_normal = false;
        return;
    }
    cnt = 0;
    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET && (cnt++<MAX_WAIT_TIMEOUT_CNT));
    if(cnt >= MAX_WAIT_TIMEOUT_CNT){
        is_oled_normal = false;
        return ;
    }
    I2C_SendData( 0x00 );

    cnt = 0;
    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET && (cnt++<MAX_WAIT_TIMEOUT_CNT));
    if(cnt >= MAX_WAIT_TIMEOUT_CNT){
        is_oled_normal = false;
        return ;
    }
    I2C_SendData( var );

    cnt = 0;
    while( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED ) && (cnt++<MAX_WAIT_TIMEOUT_CNT));
    if(cnt >= MAX_WAIT_TIMEOUT_CNT){
        is_oled_normal = false;
        return ;
    }
    I2C_GenerateSTOP( ENABLE );
}
__attribute__((section(".highcode")))
void OLED_Write_Data(unsigned char var)
{
    uint16_t cnt = 0;

    if(!is_oled_normal){
        return;
    }

    cnt = 0;
    while( I2C_GetFlagStatus( I2C_FLAG_BUSY ) != RESET && (cnt++<=MAX_WAIT_TIMEOUT_CNT));

    if(cnt >= MAX_WAIT_TIMEOUT_CNT){
        is_oled_normal = false;
        return ;
    }

    I2C_GenerateSTART( ENABLE );

    cnt = 0;
    while( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT )&& (cnt++<MAX_WAIT_TIMEOUT_CNT));
    I2C_Send7bitAddress( I2C_SLAVE_ADDRESS7, I2C_Direction_Transmitter );

    cnt = 0;
    while( !I2C_CheckEvent( I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )&& (cnt++<MAX_WAIT_TIMEOUT_CNT));

    cnt = 0;
    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET && (cnt++<MAX_WAIT_TIMEOUT_CNT));
    I2C_SendData( 0x40 );

    cnt = 0;
    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=  SET && (cnt++<MAX_WAIT_TIMEOUT_CNT));
    I2C_SendData( var );

    cnt = 0;
    while( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED ) && (cnt++<MAX_WAIT_TIMEOUT_CNT));
    I2C_GenerateSTOP( ENABLE );
}

//更新显存到OLED
void OLED_Refresh_Gram(void)
{
    uint8_t i,n;
    for(i=0;i<8;i++)
    {
        OLED_Write_Cmd(0xb0+i);    //设置页地址（0~7）
        OLED_Write_Cmd(0x00);      //设置显示位置—列低地址
        OLED_Write_Cmd(0x10);      //设置显示位置—列高地址
        for(n=0;n<128;n++)OLED_Write_Data(OLED_GRAM[n][i]);
    }
}

void OLED_Refresh_Gram_Line(uint8_t start_line, uint8_t end_line)
{
    uint8_t i,n;
    for(i=start_line; i<end_line; i++)
    {
        OLED_Write_Cmd(0xb0+i);    //设置页地址（0~7）
        OLED_Write_Cmd(0x00);      //设置显示位置—列低地址
        OLED_Write_Cmd(0x10);      //设置显示位置—列高地址
        for(n=0;n<128;n++)OLED_Write_Data(OLED_GRAM[n][i]);
    }
}

/*******************************************************************
 * @name       :void OLED_Clear(unsigned dat)
 * @function   :清除OLED屏幕显示
 * @retvalue   :无
********************************************************************/
void OLED_Clear(void)
{
    uint8_t i,n;
    for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;
    OLED_Refresh_Gram();//更新显示
}

void OLED_Clear_By_Line(uint8_t start_line, uint8_t end_line)
{
    uint8_t i,n;
    for(i=start_line;i<end_line;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;
    OLED_Refresh_Gram();//更新显示
}

/*******************************************************************
 * @name       :OledInit
 * @function   :初始化OLED SSD1306控制IC
 * @parameters :无
 * @retvalue   :无
********************************************************************/
void OledInit(void)
{

    OLED_Write_Cmd(0xAE); //关闭显示
    OLED_Write_Cmd(0xD5); //设置时钟分频因子,震荡频率
    OLED_Write_Cmd(0xF0);   //[3:0],分频因子;[7:4],震荡频率高震荡
   // OLED_Write_Cmd(0x80);   //[3:0],分频因子;[7:4],震荡频率
    OLED_Write_Cmd(0xA8); //设置驱动路数
    OLED_Write_Cmd(0X3F); //默认0X3F(1/64)
    OLED_Write_Cmd(0xD3); //设置显示偏移
    OLED_Write_Cmd(0X00); //默认为0

    OLED_Write_Cmd(0x40); //设置显示开始行 [5:0],行数.

    OLED_Write_Cmd(0x8D); //电荷泵设置
    OLED_Write_Cmd(0x14); //bit2，开启/关闭
    OLED_Write_Cmd(0x20); //设置内存地址模式
    OLED_Write_Cmd(0x02); //[1:0],00，列地址模式;01，行地址模式;10,页地址模式;默认10;
    //正向
//    OLED_Write_Cmd(0xA1); //段重定义设置,bit0:0,0->0;1,0->127;
//    OLED_Write_Cmd(0xC0); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
    OLED_Write_Cmd(0xA0); //段重定义设置,bit0:0,0->0;1,0->127;
    OLED_Write_Cmd(0xC8); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
    OLED_Write_Cmd(0xDA); //设置COM硬件引脚配置
    OLED_Write_Cmd(0x12); //[5:4]配置

    OLED_Write_Cmd(0x81); //对比度设置
    OLED_Write_Cmd(0xEF); //1~255;默认0X7F (亮度设置,越大越亮)
    OLED_Write_Cmd(0xD9); //设置预充电周期
    OLED_Write_Cmd(0xf1); //[3:0],PHASE 1;[7:4],PHASE 2;
    OLED_Write_Cmd(0xDB); //设置VCOMH 电压倍率
    OLED_Write_Cmd(0x30); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;

    OLED_Write_Cmd(0xA4); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
    OLED_Write_Cmd(0xA6); //设置显示方式;bit0:1,反相显示;0,正常显示
    OLED_Clear();
    OLED_Write_Cmd(0xAF); //需要清屏再开启显示



//    OLED_Write_Cmd(0xAE); //关闭显示
//
//        OLED_Write_Cmd(0xD5); //设置时钟分频因子,震荡频率
//        OLED_Write_Cmd(80);   //[3:0],分频因子;[7:4],震荡频率
//        OLED_Write_Cmd(0xA8); //设置驱动路数
//        OLED_Write_Cmd(0X3F); //默认0X3F(1/64)
//        OLED_Write_Cmd(0xD3); //设置显示偏移
//        OLED_Write_Cmd(0X00); //默认为0
//
//        OLED_Write_Cmd(0x40); //设置显示开始行 [5:0],行数.
//
//        OLED_Write_Cmd(0x8D); //电荷泵设置
//        OLED_Write_Cmd(0x14); //bit2，开启/关闭
//        OLED_Write_Cmd(0x20); //设置内存地址模式
//        OLED_Write_Cmd(0x02); //[1:0],00，列地址模式;01，行地址模式;10,页地址模式;默认10;
//        //正向
//    //    OLED_Write_Cmd(0xA1); //段重定义设置,bit0:0,0->0;1,0->127;
//    //    OLED_Write_Cmd(0xC0); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
//        OLED_Write_Cmd(0xA0); //段重定义设置,bit0:0,0->0;1,0->127;
//        OLED_Write_Cmd(0xC8); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
//        OLED_Write_Cmd(0xDA); //设置COM硬件引脚配置
//        OLED_Write_Cmd(0x12); //[5:4]配置
//
//        OLED_Write_Cmd(0x81); //对比度设置
//        OLED_Write_Cmd(0x7f); //1~255;默认0X7F (亮度设置,越大越亮)
//        OLED_Write_Cmd(0xD9); //设置预充电周期
//        OLED_Write_Cmd(0xf1); //[3:0],PHASE 1;[7:4],PHASE 2;
//        OLED_Write_Cmd(0xDB); //设置VCOMH 电压倍率
//        OLED_Write_Cmd(0x30); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
//
//        OLED_Write_Cmd(0xA4); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
//        OLED_Write_Cmd(0xA6); //设置显示方式;bit0:1,反相显示;0,正常显示



//        OLED_Clear();
//        OLED_Write_Cmd(0xAF); //开启显示
        //while(1);
}

/*******************************************************************
 * @name       :void OLED_Display_On(void)
 * @function   :打开OLED显示
 * @parameters :无
 * @retvalue   :无
********************************************************************/
void OLED_Display_On(void)
{
    OLED_Write_Cmd(0X8D);  //SET DCDC命令
    OLED_Write_Cmd(0X14);  //DCDC ON
    OLED_Write_Cmd(0XAF);  //DISPLAY ON
}

/*******************************************************************
 * @name       :void OLED_Display_Off(void)
 * @function   :关闭OLED显示
 * @parameters :无
 * @retvalue   :无
********************************************************************/
void OLED_Display_Off(void)
{
    OLED_Write_Cmd(0X8D);  //SET DCDC命令
    OLED_Write_Cmd(0X10);  //DCDC OFF
    OLED_Write_Cmd(0XAE);  //DISPLAY OFF
}

/*******************************************************************
 * @name       :void OLED_DrawPoint(u8 x,u8 y,u8 color)
 * @function   :在OLED屏幕中绘制点
 * @parameters :x:点的x坐标0~127
                y:点的y坐标0~63
                t:1 填充 0,清空
 * @retvalue   :无
********************************************************************/
void OLED_DrawPoint(uint8_t x,uint8_t y,uint8_t t)
{
    uint8_t pos,bx,temp=0;
    if(x>127||y>63)return;//超出范围了.
    pos=7-y/8;
    bx=y%8;
    temp=1<<(7-bx);
    if(t)OLED_GRAM[x][pos]|=temp;
    else OLED_GRAM[x][pos]&=~temp;
}

/**
 * 函    数：OLED画线
 * 参    数：X0 指定一个端点的横坐标，范围：0~127
 * 参    数：Y0 指定一个端点的纵坐标，范围：0~63
 * 参    数：X1 指定另一个端点的横坐标，范围：0~127
 * 参    数：Y1 指定另一个端点的纵坐标，范围：0~63
 * 返 回 值：无
 * 说    明：调用此函数后，要想真正地呈现在屏幕上，还需调用更新函数
 */
void OLED_DrawLine(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1)
{
    int16_t x, y, dx, dy, d, incrE, incrNE, temp;
    int16_t x0 = X0, y0 = Y0, x1 = X1, y1 = Y1;
    uint8_t yflag = 0, xyflag = 0;

    if (y0 == y1) // 横线单独处理
    {
        /*0号点X坐标大于1号点X坐标，则交换两点X坐标*/
        if (x0 > x1) {
            temp = x0;
            x0   = x1;
            x1   = temp;
        }

        /*遍历X坐标*/
        for (x = x0; x <= x1; x++) {
            OLED_DrawPoint(x, y0, 1); // 依次画点
        }
    } else if (x0 == x1) // 竖线单独处理
    {
        /*0号点Y坐标大于1号点Y坐标，则交换两点Y坐标*/
        if (y0 > y1) {
            temp = y0;
            y0   = y1;
            y1   = temp;
        }

        /*遍历Y坐标*/
        for (y = y0; y <= y1; y++) {
            OLED_DrawPoint(x0, y, 1); // 依次画点
        }
    } else // 斜线
    {
        /*使用Bresenham算法画直线，可以避免耗时的浮点运算，效率更高*/
        /*参考文档：https://www.cs.montana.edu/courses/spring2009/425/dslectures/Bresenham.pdf*/
        /*参考教程：https://www.bilibili.com/video/BV1364y1d7Lo*/

        if (x0 > x1) // 0号点X坐标大于1号点X坐标
        {
            /*交换两点坐标*/
            /*交换后不影响画线，但是画线方向由第一、二、三、四象限变为第一、四象限*/
            temp = x0;
            x0   = x1;
            x1   = temp;
            temp = y0;
            y0   = y1;
            y1   = temp;
        }

        if (y0 > y1) // 0号点Y坐标大于1号点Y坐标
        {
            /*将Y坐标取负*/
            /*取负后影响画线，但是画线方向由第一、四象限变为第一象限*/
            y0 = -y0;
            y1 = -y1;

            /*置标志位yflag，记住当前变换，在后续实际画线时，再将坐标换回来*/
            yflag = 1;
        }

        if (y1 - y0 > x1 - x0) // 画线斜率大于1
        {
            /*将X坐标与Y坐标互换*/
            /*互换后影响画线，但是画线方向由第一象限0~90度范围变为第一象限0~45度范围*/
            temp = x0;
            x0   = y0;
            y0   = temp;
            temp = x1;
            x1   = y1;
            y1   = temp;

            /*置标志位xyflag，记住当前变换，在后续实际画线时，再将坐标换回来*/
            xyflag = 1;
        }

        /*以下为Bresenham算法画直线*/
        /*算法要求，画线方向必须为第一象限0~45度范围*/
        dx     = x1 - x0;
        dy     = y1 - y0;
        incrE  = 2 * dy;
        incrNE = 2 * (dy - dx);
        d      = 2 * dy - dx;
        x      = x0;
        y      = y0;

        /*画起始点，同时判断标志位，将坐标换回来*/
        if (yflag && xyflag) {
            OLED_DrawPoint(y, -x, 1);
        } else if (yflag) {
            OLED_DrawPoint(x, -y, 1);
        } else if (xyflag) {
            OLED_DrawPoint(y, x, 1);
        } else {
            OLED_DrawPoint(x, y, 1);
        }

        while (x < x1) // 遍历X轴的每个点
        {
            x++;
            if (d < 0) // 下一个点在当前点东方
            {
                d += incrE;
            } else // 下一个点在当前点东北方
            {
                y++;
                d += incrNE;
            }

            /*画每一个点，同时判断标志位，将坐标换回来*/
            if (yflag && xyflag) {
                OLED_DrawPoint(y, -x, 1);
            } else if (yflag) {
                OLED_DrawPoint(x, -y, 1);
            } else if (xyflag) {
                OLED_DrawPoint(y, x, 1);
            } else {
                OLED_DrawPoint(x, y, 1);
            }
        }
    }
}

/**
 * 函    数：OLED矩形
 * 参    数：X 指定矩形左上角的横坐标，范围：0~127
 * 参    数：Y 指定矩形左上角的纵坐标，范围：0~63
 * 参    数：Width 指定矩形的宽度，范围：0~128
 * 参    数：Height 指定矩形的高度，范围：0~64
 * 参    数：IsFilled 指定矩形是否填充
 *           范围：OLED_UNFILLED       不填充
 *                 OLED_FILLED          填充
 * 返 回 值：无
 * 说    明：调用此函数后，要想真正地呈现在屏幕上，还需调用更新函数
 */
void OLED_DrawRectangle(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height, uint8_t IsFilled)
{
    uint8_t i, j;
    if (!IsFilled) // 指定矩形不填充
    {
        /*遍历上下X坐标，画矩形上下两条线*/
        for (i = X; i < X + Width; i++) {
            OLED_DrawPoint(i, Y, 1);
            OLED_DrawPoint(i, Y + Height - 1, 1);
        }
        /*遍历左右Y坐标，画矩形左右两条线*/
        for (i = Y; i < Y + Height; i++) {
            OLED_DrawPoint(X, i, 1);
            OLED_DrawPoint(X + Width - 1, i, 1);
        }
    } else // 指定矩形填充
    {
        /*遍历X坐标*/
        for (i = X; i < X + Width; i++) {
            /*遍历Y坐标*/
            for (j = Y; j < Y + Height; j++) {
                /*在指定区域画点，填充满矩形*/
                OLED_DrawPoint(i, j, 1);
            }
        }
    }
}

/*******************************************************************
 * @name       :void OLED_Fill(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t dot)
 * @function   :填充指定区域
 * @parameters :x1:指定填充区域的起始x坐标
                y1:指定填充区域的起始y坐标
                x2:指定填充区域的结束x坐标
                y2:指定填充区域的结束y坐标
                dot:0,清空;1,填充
 * @retvalue   :无
********************************************************************/
void OLED_Fill(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t dot)
{
    uint8_t x,y;
    for(x=x1;x<=x2;x++)
    {
        for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);
    }
  //  OLED_Refresh_Gram();//更新显示
}



/*****************************************************************************
 * @name       :void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode)
 * @function   :显示单个英文字符
 * @parameters :x:字符显示位置的起始x坐标0~127
                y:字符显示位置的起始y坐标0~63
                chr:显示字符的ascii码（0～94）
                size:选择字体 12/16/24
                mode:0,反白显示;1,正常显示
 * @retvalue   :无
******************************************************************************/
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode)
{
    uint8_t temp,t,t1;
    uint8_t y0=y;
    uint8_t csize=(size/8+((size%8)?1:0))*(size/2);     //得到字体一个字符对应点阵集所占的字节数
    chr=chr-' ';//得到偏移后的值

    if(x>MAX_COLUMN-1)
     {
       x=0;
       y=y+2;
     }

    for(t=0;t<csize;t++)
    {
        if(size==16)temp=asc2_1608[chr][t];         //调用1608字体
        else if(size==12)temp=asc2_1206[chr][t];    //调用1206字体
        else return;                                //没有的字库
        for(t1=0;t1<8;t1++)
        {
            if(temp&0x80)OLED_DrawPoint(x,y,mode);
            else OLED_DrawPoint(x,y,!mode);
            temp<<=1;
            y++;
            if((y-y0)==size)
            {
                y=y0;
                x++;
                break;
            }
        }
    }
}

//在指定位置显示一个中文字符
//x:
//y:
//mode:0,反白显示;1,正常显示
//size:选择字体 12/16/24
/*****************************************************************************
 * @name       :void OLED_Chinese_Text(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode)
 * @function   :显示中文字符串
 * @parameters :x:汉语字符串的起始x坐标0~127
                y:汉语字符串的起始y坐标0~63
                chr:二位字库中的列向量，第几个字
                size:中文字符串的大小
                mode:0,反白显示;1,正常显示
 * @retvalue   :无
******************************************************************************/
void OLED_Chinese_Text(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode)
{
    uint8_t temp,t,t1;
    uint8_t y0=y;
    uint8_t csize=(size/8+((size%8)?1:0))*size;     //得到字体一个字符对应点阵集所占的字节数
    for(t=0;t<csize;t++)
    {
        temp = Chinese_Text[chr][t];
        for(t1=0;t1<8;t1++)
        {
            if(temp&0x80)OLED_DrawPoint(x,y,mode);
            else OLED_DrawPoint(x,y,!mode);
            temp<<=1;
            y++;
            if((y-y0)==size)
            {
                y=y0;
                x++;
                break;
            }
        }
    }
}


void OLED_Chinese_Text_Str_Test(uint8_t x, uint8_t y, char *word_str){
    uint16_t GBK_indax = -1;
    uint8_t word_high;
    uint8_t word_low;

    if (word_str == NULL) {
        return;
    }

    dk_log_hex("word_str", word_str, strlen(word_str));

    for(int n=0; word_str[n]!='\0'; n++)  //ҩzؖػԮìא/ؖػìƐ׏۳ؖҰˤԶ
    {
        //遇到字符
        if(word_str[n]>31&&word_str[n]<127){
            if(x+16 >= MAX_COLUMN){ //自动换行
                x=0;y+=16;
            }
            OLED_ShowChar(x, y, word_str[n], 16, 1);

            x+=8;
            continue; //能少写一个else{}
        }
        word_high = word_str[n];
        word_low = word_str[n+1];
      //  PRINT("word_str[n]:%x,word_str[n+1]%x\r\n", word_high, word_low);
        /*汉字编码查表*/
        for (GBK_indax=0; font_16x16_GBK[GBK_indax]!='\0'; GBK_indax++) {
            if ( font_16x16_GBK[GBK_indax] == (((uint16_t)word_high << 8) | (uint16_t)word_low) ) {
                DEBUG_Printf("GBK_indax:%d\r\n", GBK_indax);
                break;

            }
        }

        if(x+16 >= MAX_COLUMN){
            x =0;
            y += HZ_WIDTH;
        }
        for(int i = 0; i < 16; i++){
            for (int j = 0; j < 8; j++){            //سѫҟ
                if (font_16x16[GBK_indax][2*i]&(0x80>>j)) //ӲΪאτ2ٶؖޚìسӠǡۃȫΪż˽ìԒӠȫΪǦ˽
                    OLED_DrawPoint(x+j, y+i, 1);        //˽ةאìÿlѸք}ٶԪ̘ì֣һѐc
#ifndef BACKGROUND_COLOR_DIS
                else                                //ڊ׸Ǧ˽Ԫ̘؜ˇغհسѫѐìż˽غհԒѫѐ
                    OLED_DrawPoint(x+j, y+i, 0);
#endif
            }
            for (int j = 0; j < 8; j++){              //ԒѫҟìԒӠΪǦ˽
                if (font_16x16[GBK_indax][2*i+1]&(0x80>>j)) //k*32փս՚ؖģ˽ةאքԵʼЂҪ
                    OLED_DrawPoint(x+j+8,y+i,1);
#ifndef BACKGROUND_COLOR_DIS
                else
                    OLED_DrawPoint(x+j+8,y+i,0);
#endif
            }
        }
        x+=16;  //ټтλ׃
        n+=1;   //ϒ֢oһٶںؖռ3ٶλ׃ì̹Ӕݓ2
    }
}


//显示汉字
//由于汉字是16*16大小的，所以最多显示4行汉字
//index:在汉字取模中的索引
void OLED_Chinese_Text_Str(uint8_t x,uint8_t y, const uint8_t *index_table, uint8_t index_num)
{
    int i = 0;

    for(i = 0; i < index_num; i++){
        if(x+i*HZ_WIDTH >127 || y>63){
            break;
        }
        OLED_Chinese_Text(x+i*HZ_WIDTH, y, index_table[i], HZ_WIDTH, 1);
    }
}


/*****************************************************************************
 * @name       :u32 mypow(uint8_t m,uint8_t n)
 * @function   :获取m的n次方（内部调用）
 * @parameters :m:the multiplier
                n:the power
 * @retvalue   :m的n次方
******************************************************************************/
static uint32_t mypow(uint8_t m,uint8_t n)
{
    uint32_t result=1;
    while(n--)result*=m;
    return result;
}

/*****************************************************************************
 * @name       :void OLED_ShowNum(uint8_t x,uint8_t y,u32 num,uint8_t len,uint8_t size)
 * @function   :显示数字
 * @parameters :x:数字的起始x坐标
                y:数字的起始y坐标
                num:数字（0～4294967295）
                len:显示数字的长度
                Size:显示数字的大小
 * @retvalue   :无
******************************************************************************/
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size)
{
    uint8_t t,temp;
    uint8_t enshow=0;
    for(t=0;t<len;t++)
    {
        temp=(num/mypow(10,len-t-1))%10;
        if(enshow==0&&t<(len-1))
        {
            if(temp==0)
            {
                OLED_ShowChar(x+(size/2)*t,y,' ',size,1);
                continue;
            }else enshow=1;

        }
        OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1);
    }
}




/*****************************************************************************
 * @name       :void OLED_ShowString(uint8_t x,uint8_t y,const uint8_t *p,uint8_t size)
 * @function   :显示英文字符串
 * @parameters :x:英语字符串的起始x坐标
                y:英语字符串的起始y坐标
                *p:字符串起始地址
                size:显示字符的大小
 * @retvalue   :无
******************************************************************************/
void OLED_ShowString(uint8_t x,uint8_t y,const uint8_t *p,uint8_t size)
{
    while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
    {
        if(x>(128-(size/2))){x=0;y+=size;}
        if(y>(64-size)){y=x=0;OLED_Clear();}
        OLED_ShowChar(x,y,*p,size,1);
        x+=size/2;
        p++;
    }

}

/******************************************************************************
      函数说明：显示图片
      入口数据：x,y起点坐标
                length 图片长度
                width  图片宽度
                pic[]  图片数组
      返回值：  无
******************************************************************************/
void LCD_ShowPicture(uint16_t x,uint16_t y,uint16_t length,uint16_t width,const uint8_t pic[])
{

    UINT8 temp, i, j;
    UINT8 y0 = y;
    UINT8 charsize = length*width/8;

    for (i = 0; i < charsize; ++i)
    {
        temp = pic[i];
        for (j = 0; j < 8; ++j)
        {
            if (temp & 0x80)
                OLED_DrawPoint(x, y, 1);
            else
                OLED_DrawPoint(x, y, 0);

            temp <<= 1;
            ++y;
            if ((y - y0) == length)
            {
                y = y0;
                ++x;
                break;
            }
        }
    }

}

//可以正常显示电量
void OledDisplayTest(void)
{

    I2cInit();
    PRINT("I2cInit\r\n");
    OledInit();

    PRINT("OledInit\r\n");

    OLED_Clear();

    showBatteryValueStr(50);

    OLED_Refresh_Gram();
}
void OledPowerOff(void){
    if(!is_init_flag){
        return;
    }
    OLED_Clear();
    I2cUninit();

    oled_power_status = false;
}
void OledPowerOn(void){
    if(is_init_flag){
        return;
    }
    I2cInit();
    DEBUG_Printf("I2cInit\r\n");

    OledInit();
    DEBUG_Printf("OledInit\r\n");

    oled_power_status = true;
}

//1:亮屏，0:关屏
uint8_t get_oled_power_status(void){
    return oled_power_status;
}
#endif
#else
void OledForceUninit(void)
{
    GPIO_ModeCfg(OLED_BL_EN_PORT, OLED_BL_EN_PIN, GPIO_ModeOut_PP_5mA);
    OLED_BL_EN_H();
    GPIOB_ModeCfg( GPIO_Pin_21|GPIO_Pin_20, GPIO_ModeIN_PD );
}
#endif
